package thirdapi

import (
	"advertising/config"
	"advertising/config/redis"
	"advertising/internal/define"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"strings"
	"time"

	"192.168.1.75/go-pkg/errorx"
	"192.168.1.75/go-pkg/helper"
	"192.168.1.75/go-pkg/logx"
)

type YiShou struct {
	appid   string // appid
	appkey  string // appkey
	version string // API版本号，目前版本号为 1.8
}

func NewYiShou() *YiShou {
	return &YiShou{
		appid:   config.Config.Yishou.AppID,
		appkey:  config.Config.Yishou.Appkey,
		version: "1.8",
	}
}

const (
	YISHOU_FILE_PREFIX = "http://file2.pingxiaobao.com"
	// URL
	URL_HEART           = "https://api2.eselltech.com/api/datac/device/online.shtml"          // 2.3 心跳接口
	URL_SUBSCRIBE       = "https://api6.pingxiaobao.com/rtb/subscribe.shtml"                  // 2.4 广告订阅接口
	URL_DEVICE_SAVE     = "https://boss.eselltech.cn/api/pxb/third/med/device/save.shtml"     // 2.5 设备备案接口
	URL_DEVICE_DEL      = "https://boss.eselltech.cn/api/pxb/third/med/device/del.shtml"      // 2.6 设备下线接口
	URL_DEVICE_SLOTINFO = "https://boss.eselltech.cn/api/pxb/third/med/device/slotInfo.shtml" // 2.7 设备广告位信息接口
)

// appid
func (ys *YiShou) getAppID() string {
	return ys.appid
}

// appkey
func (ys *YiShou) getAppKey() string {
	return ys.appkey
}

// API版本号
func (ys *YiShou) getVersion() string {
	return ys.version
}

// 报文发出时间戳
func (ys *YiShou) getTimeStamp() int64 {
	timeNow := time.Now().Unix()
	return timeNow
}

func PostFormURLEncoded(url string, data url.Values) (res []byte, err error) {
	resp, err := http.Post(url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode()))
	if err != nil {
		return res, errorx.New("请求失败", -1)
	}
	defer resp.Body.Close()
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return res, errorx.New(err.Error(), -1)
	}
	return body, err
}

func (ys *YiShou) buildQuery(params, device string) string {
	appID := ys.getAppID()
	appKey := ys.getAppKey()
	timestamp := ys.getTimeStamp()
	version := ys.getVersion()

	str := fmt.Sprintf("appid=%s&appkey=%s&payload=%s&sequence=%d&timestamp=%d&uuid=%s&version=%s",
		appID, appKey, params, timestamp, timestamp, device, version)
	sign := helper.Md5(str)

	queryString := fmt.Sprintf("?appid=%s&sequence=%d&timestamp=%d&uuid=%s&version=%s&sign=%s",
		appID, timestamp, timestamp, device, version, sign)
	return queryString
}

type YiShouHeartSlot struct {
	SlotID     int64  `json:"slot-id"`     // 广告位id
	Cycle      string `json:"cycle"`       // 【可选】 播放方式 1 - 轮播，2 - 按天，3 - 按周
	CycleNum   int64  `json:"cycle-num"`   // 【可选】 轮播 5 - 5轮播，8 - 8轮播
	SurplusNum int64  `json:"surplus-num"` // 【可选】 轮播时为当前剩余库存数
}
type YiShouHeartReq struct {
	DeviceUUID string            `json:"device-uuid"` // 设备编号
	UpTime     string            `json:"up-time"`     // 设备开机时间
	IP         string            `json:"ip"`          // IP
	Longitude  float64           `json:"longitude"`   // 经度
	Latitude   float64           `json:"latitude"`    // 纬度
	Slots      []YiShouHeartSlot `json:"slots"`       // 设备所在广告位
}

type YiShouHeartRspPayload struct {
	NextTime string `json:"next-time"` // 下次上报心跳时间 2024-01-30 18:56:19
}

type YiShouHeartRsp struct {
	Code    int64                 `json:"code"`
	Message string                `json:"message"`
	Payload YiShouHeartRspPayload `json:"payload"`
}

// 心跳接口
// 提供给媒体设备每日定时上报心跳状态，为设备质量提供数据依据。
// 疏报：每日3次。开机后1小时内或上午7-8点；11-12点第二次上报；16-17点或关机前1小时内第三次上报；
// 密报：每小时上报1次。
func (ys *YiShou) Heart(ctx context.Context, DeviceUUID, UpTime, IP string, Longitude, Latitude float64, slots []YiShouHeartSlot) (res YiShouHeartRspPayload, err error) {
	params := YiShouHeartReq{
		DeviceUUID: DeviceUUID,
		UpTime:     UpTime,
		IP:         IP,
		Longitude:  Longitude,
		Latitude:   Latitude,
		Slots:      slots,
	}
	bytes, _ := json.Marshal(params)
	QueryURL := URL_HEART + ys.buildQuery(string(bytes), DeviceUUID)

	paramsData := url.Values{}
	paramsData.Add("payload", string(bytes))
	body, err := PostFormURLEncoded(QueryURL, paramsData)
	if err != nil {
		return
	}
	var rsp YiShouHeartRsp
	json.Unmarshal(body, &rsp)
	if rsp.Code < 0 {
		return rsp.Payload, errorx.New(rsp.Message, int(rsp.Code))
	}
	return rsp.Payload, nil
}

type YiShouSubscribeReq struct {
	DeviceUUID string  `json:"device-uuid"` // 设备编号
	SlotID     string  `json:"slot-id"`     // 广告位id
	Quantity   int64   `json:"quantity"`    // 期望获取广告数量(1-9，返回的广告条数小于等于quantity)
	Type       string  `json:"type"`        // 【可选】广告类型，IMG - 图片，VDO - 视频
	IP         string  `json:"ip"`          // 【可选】IP
	Longitude  float64 `json:"longitude"`   // 【可选】经度
	Latitude   float64 `json:"latitude"`    // 【可选】纬度
}

type YiShouSubscribeRspPayload struct {
	URL        string `json:"url"`         // 广告内容URL
	Width      int    `json:"width"`       // 宽度（像素）
	Height     int    `json:"height"`      // 高度（像素）
	Sign       string `json:"sign"`        // 广告内容签名（MD5，确保信息安全）
	TrackUrl   string `json:"track-url"`   // 每条广告的唯一效果通知URL，有效期30分钟，广告播放完调用
	SlotID     string `json:"slot-id"`     // 广告位ID
	ShowTime   int    `json:"show-time"`   // 播放时长，秒
	ExpireTime string `json:"expire-time"` // 广告投放过期时间，yyyyMMdd HH:mm:SS
	FileSize   string `json:"file-size"`   // 文件大小，KB
	Type       string `json:"type"`        // 与广告位对应的素材类型（非请求参数中的类型）
	AdID       int    `json:"ad-id"`       // 广告ID
	ID         string `json:"id"`          // 唯一ID
}
type YiShouSubscribeRsp struct {
	Code    int64                       `json:"code"`
	Message string                      `json:"message"`
	Payload []YiShouSubscribeRspPayload `json:"payload"`
}

// 广告订阅接口
func (ys *YiShou) Subscribe(ctx context.Context, DeviceUUID string, slotID string) (res []YiShouSubscribeRspPayload, err error) {
	rdb := redis.NewDB()
	// 先读缓存
	subscribeCache, _ := rdb.Get(context.Background(), define.RedisYiShouDeviceSubscribePrefix+DeviceUUID).Result()
	if subscribeCache != "" {
		var rsp YiShouSubscribeRsp
		json.Unmarshal([]byte(subscribeCache), &rsp)
		if rsp.Code < 0 {
			return rsp.Payload, errorx.New(rsp.Message, int(rsp.Code))
		}
		return rsp.Payload, nil
	}
	params := YiShouSubscribeReq{
		DeviceUUID: DeviceUUID,
		SlotID:     slotID,
		Quantity:   9,
	}
	bytes, _ := json.Marshal(params)
	QueryURL := URL_SUBSCRIBE + ys.buildQuery(string(bytes), DeviceUUID)

	paramsData := url.Values{}
	paramsData.Add("payload", string(bytes))
	body, err := PostFormURLEncoded(QueryURL, paramsData)
	if err != nil {
		return
	}
	var rsp YiShouSubscribeRsp
	json.Unmarshal(body, &rsp)

	// 缓存半小时,避免重复调用
	yishouRsp, _ := json.Marshal(rsp)
	rdb.Set(context.Background(), define.RedisYiShouDeviceSubscribePrefix+DeviceUUID, string(yishouRsp), time.Duration(1800)*time.Second)
	if rsp.Code < 0 {
		return rsp.Payload, errorx.New(rsp.Message, int(rsp.Code))
	}
	return rsp.Payload, nil
}

type YiShouDeviceSaveReq struct {
	List []struct {
		// 必填
		ThirdId        string `json:"thirdId"`        // 设备编号 (MAC地址或可唯一识别设备的编号)「新增/更新 必填」「最大字符长度64」「例如:1b1187610dnnjje3」
		DeviceTypeName string `json:"deviceTypeName"` // 设备类型名称(请按照易售设备类型附表选择进行填写。不能填写其他)「新增 必填」「最大字符长度16」「例如:落地大屏」
		ScreenType     string `json:"screenType"`     // 屏幕类型「新增 必填」「可选值范围:LCD、LED、图片」「例如:LCD」
		SceneName      string `json:"sceneName"`      // 设备所在场景(请按照易售场景附表选择进行填写。不能填写其他)「新增 必填」「多个用英文逗号隔开」「最大字符长度64」「例如:餐饮」
		Width          string `json:"width"`          // 宽 单位：像素(字段内不要写单位)「电子屏-新增 必填」「最大字符长度16」「例如:1080」
		Height         string `json:"height"`         // 高 单位：像素(字段内不要写单位)「电子屏-新增 必填」「最大字符长度16」「例如:1920」
		WidthCm        string `json:"widthCm"`        // 宽 单位：厘米(保留正整数，字段内不要写单位)「非电子屏-新增 必填」「最大字符长度16」「例如:54 」
		HeightCm       string `json:"heightCm"`       // 高 单位：厘米(保留正整数，字段内不要写单位)「非电子屏-新增 必填」「最大字符长度16」「例如:31」
		Size           string `json:"size"`           // 屏幕尺寸(单位：英寸，如有小数点只保留一位小数点)「新增 必填」「最大字符长度16」「例如:24.3 」
		Province       string `json:"province"`       // 省份(设备所在省份,需以易售为准,详细查看省市区附表)「新增 必填」「最大字符长度16」「例如:广东省」
		City           string `json:"city"`           // 城市(设备所在城市,需以易售为准,详细查看省市区附表)「新增 必填」「最大字符长度16」「例如:深圳市」
		District       string `json:"district"`       // 区县(设备所在区/县,需以易售为准,详细查看省市区附表)「新增 必填」「最大字符长度16」「例如:南山区」
		Address        string `json:"address"`        // 详细地址(设备详细地址，需包含（省市区）,完整的详细地址，可精确到门栋单元)「新增 必填」「最大字符长度500」
		ScreenQuantity string `json:"screenQuantity"` // 屏幕数或点位数(设备的屏幕数量)「新增 必填」「最大字符长度16」「默认值:1」「例如:1」
		HasSound       string `json:"hasSound"`       // 是否有声音(设备播放视频广告时，有无声音)「电子屏-新增 必填」「最大字符长度16」「说明:是/否」「例如:是」
		TurnOnTime     string `json:"turnOnTime"`     // 开机时间(如开机时段不唯一，最早开机时间)「电子屏-新增 必填」「最大字符长度16」「格式如:00:00:00」「例如:00:00:00」
		TurnOffTime    string `json:"turnOffTime"`    // 关机时间(如关机时段不唯一，最晚关机时间)「电子屏-新增 必填」「最大字符长度16」「格式如:23:59:59」例如:23:59:59」
		IsNet          string `json:"isNet"`          // 是否联网「电子屏-新增 必填」「最大字符长度16」「说明:Y/N」「例如:Y」
		IsSale         string `json:"isSale"`         // 是否可售(可投放广告)「电子屏-新增 必填」「最大字符长度16」「说明:Y/N」「例如:Y」
		SaleType       string `json:"saleType"`       // 售卖方式「电子屏-新增 必填」「最大字符长度16」「可选值范围:CPM、CPT、CPM/CPT」「例如:CPM」
		MinSalesCycle  string `json:"minSalesCycle"`  // 最小售卖周期「非电子屏-新增 必填」「最大字符长度16」「默认值:周」「可选值范围:周、双周、月、天」「例如:周」
		Category       string `json:"category"`       // 品类(非电子屏指线下印刷相关广告屏)「新增-必填」「最大字符长度16」「可选值范围:电子屏、非电子屏」「例如:电子屏」
		DeviceRemark   string `json:"deviceRemark"`   // 设备备注(记录相关说明事宜)「最大字符长度100」「非必填」
		// 非必填
		Name               string `json:"name"`               // 设备名称 「最大字符长度64」「例如:易售测试设备」
		Position           string `json:"position"`           // 点位详细安装位置(如：30楼左侧电梯)「最大字符长度500」「例如:1102大会议室」
		Poi                string `json:"poi"`                // poi名称(百度地图对应的POI名称)「最大字符长度64」「例如:创维大厦」
		PoiAddress         string `json:"poiAddress"`         // poi地址(填写百度地图对应的POI地址)「最大字符长度64」「例如:广东省深圳市南山区高新南一道8号」
		Longitude          string `json:"longitude"`          // 经度(设备所在经度)「最大字符长度16」「例如:130.4837545」
		Latitude           string `json:"latitude"`           // 维度(设备所在纬度)「最大字符长度16」「例如:22.02394035」
		SeeNum             string `json:"seeNum"`             // 每日接触人数(大概的每日接触人数)「最大字符长度16」「例如:100」
		SlotSizes          string `json:"slotSizes"`          // 广告位规格(该设备支持的广告位规格，必须是该媒体当前设备类型下的广告位规格,多个请用英文逗号分隔)「最大字符长度64」「例如:1080*1920」
		PvCpt              string `json:"pvCpt"`              // CPT播放次数(CPT设备每日能提供给易售的最大播放次数)「最大字符长度16」「例如:300」
		SaleableNumCpt     string `json:"saleableNumCpt"`     // CPT可售卖份数(CPT设备每日广告位数,几个广告轮播一次的过程中，可售卖的份数)「最大字符长度16」「例如:10」
		PublicationDateCpt string `json:"publicationDateCpt"` // CPT上刊日期(CPT设备上刊日期,多个请用英文逗号分隔)「最大字符长度16」「可选值范围:无限制、星期一、星期二、...星期天」「例如:无限制」
		AdSystem           string `json:"adSystem"`           // 广告系统(系统安装的广告播放系统)「最大字符长度16」「可选值范围:易信发APP、易信发SDK、第三方APP、第三方SDK、未知」「例如:易信发APP」
		AccessMode         string `json:"accessMode"`         // 接入方式(设备接入的方式)「最大字符长度16」「可选值范围:API、SDK、未程序化对接」「例如:未程序化对接」
		PositionAttr       string `json:"positionAttr"`       // 位置属性(用于记录设备的位置属性,如:民宿、宾馆、住宅、酒店级别等)「最大字符长度50」
		MerchantNo         string `json:"merchantNo"`         // 商家编号(用于记录设备的商家编号)「最大字符长度50」
	} `json:"list"`
}

// 设备备案接口
func (ys *YiShou) DeviceSave() { // 无返回
}

type YiShouDeviceDelReq struct {
	List []string `json:"list"` // 入库设备id
}

// 设备下线接口
func (ys *YiShou) DeviceDel(ctx context.Context, deviceID []string) (err error) {
	return
}

type YiShouDeviceSlotInfoReq struct {
	DeviceUUID string `json:"deviceUuid"` // 设备编号
}

type YiShouDeviceSlotInfoRspPayload struct {
	SlotID         int64  `json:"slotId"`         // 广告位id
	DeviceTypeName string `json:"deviceTypeName"` // 设备类型名称
	Width          int64  `json:"width"`          // 广告位宽(像素)
	Height         int64  `json:"height"`         // 广告位高(像素)
	Type           string `json:"type"`           // 素材类型（IMG：图片，VDO：视频，IMGVDO：图片视频）
}
type YiShouDeviceSlotInfoRsp struct {
	Code    int64                            `json:"code"`
	Message string                           `json:"message"`
	Payload []YiShouDeviceSlotInfoRspPayload `json:"payload"`
}

// 设备广告位信息接口
func (ys *YiShou) DeviceSlotInfo(ctx context.Context, DeviceUUID string) (res []YiShouDeviceSlotInfoRspPayload, err error) {
	rdb := redis.NewDB()
	// 先读缓存
	slotCache, _ := rdb.Get(context.Background(), define.RedisYiShouDeviceSlotInfoPrefix+DeviceUUID).Result()
	if slotCache != "" {
		var rsp YiShouDeviceSlotInfoRsp
		json.Unmarshal([]byte(slotCache), &rsp)
		if rsp.Code < 0 {
			return rsp.Payload, errorx.New(rsp.Message, int(rsp.Code))
		}
		return rsp.Payload, nil
	}
	params := YiShouDeviceSlotInfoReq{
		DeviceUUID: DeviceUUID,
	}
	bytes, _ := json.Marshal(params)
	QueryURL := URL_DEVICE_SLOTINFO + ys.buildQuery(string(bytes), DeviceUUID)
	paramsData := url.Values{}
	paramsData.Add("payload", string(bytes))
	body, err := PostFormURLEncoded(QueryURL, paramsData)
	if err != nil {
		return
	}
	var rsp YiShouDeviceSlotInfoRsp
	json.Unmarshal(body, &rsp)
	logx.Info(ctx, "", logx.Any("req", QueryURL), logx.Any("rsp", rsp))

	// 缓存半小时,避免重复调用
	yishouRsp, _ := json.Marshal(rsp)
	rdb.Set(context.Background(), define.RedisYiShouDeviceSlotInfoPrefix+DeviceUUID, string(yishouRsp), time.Duration(1800)*time.Second)
	if rsp.Code < 0 {
		return rsp.Payload, errorx.New(rsp.Message, int(rsp.Code))
	}
	return rsp.Payload, nil
}
